﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;

using zijian666.AnyExtensions.Privates;

namespace zijian666.AnyExtensions;

/// <summary>
/// 字符串类型的扩展方法
/// </summary>
public static partial class StringExtensions
{
    /// <summary>
    /// 指示字符串是 null 还是空字符串 ("")。
    /// </summary>
    public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) => string.IsNullOrEmpty(value);
    /// <summary>
    /// 指示字符串是 null、空还是仅由空白字符组成。
    /// </summary>
    public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) => string.IsNullOrWhiteSpace(value);
    /// <summary>
    /// 如果字符串是 null 或空字符串 ("")，则返回 <paramref name="defaultValue"/>，否则根据 <paramref name="trim"/>确定是否需要前后去空格
    /// </summary>
    public static string EmptyOrDefault(this string? value, string defaultValue, bool trim = true)
        => string.IsNullOrEmpty(value) ? defaultValue : (trim ? value!.Trim() : value!);
    /// <summary>
    /// 如果字符串是null、空或是仅由空白字符组成，则返回 <paramref name="defaultValue"/>，否则根据 <paramref name="trim"/>确定是否需要前后去空格
    /// </summary>
    public static string WhiteSpaceOrDefault(this string? value, string defaultValue, bool trim = true)
        => string.IsNullOrWhiteSpace(value) ? defaultValue : (trim ? value!.Trim() : value!);

    private static readonly char[] DEFAULT_SEPARATOR_CHARS = [',', ' ', ';', '\n', '\r', '\t'];
    /// <summary>
    /// 按照指定符号切分字符串，并转为指定格式
    /// </summary>
    public static IEnumerable<T> Split<T>(this string value, params char[] separator)
    {
        if (separator.IsNullOrEmpty())
        {
            separator = DEFAULT_SEPARATOR_CHARS;
        }
        var array = value.Split(separator, StringSplitOptions.RemoveEmptyEntries);
        if (array is T[] strArray)
        {
            return strArray;
        }
        return array.Select(InternalExtensions.ConvertTo<T>);
    }

    private static readonly ProfanityFilter LIKE_FILTER = new ProfanityFilter
    {
        ['\''] = "''",
        ['%'] = "[%]",
        ['_'] = "[_]",
        ['['] = "[[]",
        [']'] = "[]]",
    }.Compile();

    /// <summary>
    /// 返回有效的 sql like 字符串
    /// </summary>
    /// <param name="value"></param>
    public static string EncodeSqlLike(this string value) => LIKE_FILTER.Replace(value);


    private static readonly ProfanityFilter FILE_NAME_FILTER = new ProfanityFilter(
        codes => Path.GetInvalidFileNameChars().Each(x => codes[x] = "")
    )
    {
        ['\\'] = "＼",
        ['/'] = "／",
        [':'] = "：",
        ['*'] = "＊",
        ['?'] = "？",
        ['"'] = "＂",
        ['<'] = "＜",
        ['>'] = "＞",
        ['|'] = "｜",
    }.Compile();
    /// <summary>
    /// 编码为 文件名 可用的字符串
    /// </summary>
    /// <param name="value"></param>
    public static string EncodeFileName(this string value) => FILE_NAME_FILTER.Replace(value);


    /// <summary>
    /// 返回指定长度的字符串，如果超出长度，则截断并添加 <paramref name="overflow"/> 后缀
    /// </summary>
    /// <param name="value">待操作的字符串对象</param>
    /// <param name="start">开始位的索引，小于0时，表示从后往前数</param>
    /// <param name="end">结束位的索引，小于0时，表示从后往前数</param>
    /// <param name="overflow">超出长度后缀</param>
    /// <returns></returns>
    public static string? Slice(this string? value, int start, int end = 0, string? overflow = "")
    {
        if (value is null)
        {
            return "";
        }
        if (start < 0)
        {
            start = Math.Max(0, value.Length + start);
        }
        if (end == 0)
        {
            overflow = null;
            end = value.Length;
        }
        else if (end < 0)
        {
            end = value.Length + end;
        }
        if (overflow?.Length > 0)
        {
            end -= overflow.Length;
        }
        if (start >= end)
        {
            return overflow;
        }
        if (end >= value.Length)
        {
#if NET7_0_OR_GREATER
            return start == 0 ? value : value[start..];
#else
            return start == 0 ? value : value.Substring(start);
#endif
        }
#if NET7_0_OR_GREATER
        return string.Concat(value.AsSpan(start, end - start), overflow);
#else
        return value.Substring(start, end - start) + overflow;
#endif
    }

    /// <summary>
    /// 字符串脱敏
    /// </summary>
    /// <param name="value">待操作的字符串对象</param>
    /// <param name="prefix">前缀保留长度</param>
    /// <param name="postfix">后缀保留长度</param>
    /// <param name="mask">脱敏后占位字符，默认"****"</param>
    /// <returns></returns>
    public static string Masking(this string? value, int prefix, int? postfix = null, string mask = "****")
    {
        if (value is null)
        {
            return "";
        }

        if (string.IsNullOrWhiteSpace(mask))
        {
            mask = "****";
        }

        prefix = Math.Max(0, prefix);
        postfix = Math.Max(0, postfix ?? prefix);

        if (postfix == 0)
        {
            if (prefix == 0)
            {
                return mask;
            }
            if (prefix < value.Length)
            {
#if NET7_0_OR_GREATER
                return string.Concat(value.AsSpan(0, prefix), mask);
#else
                return value.Substring(0, prefix) + mask;
#endif
            }
        }

        if (prefix + postfix >= value.Length)
        {
            if (prefix > value.Length)
            {
                return value + mask;
            }

#if NET7_0_OR_GREATER
            return string.Concat(value.AsSpan(0, prefix), mask, value.AsSpan(prefix));
        }

        return string.Concat(value.AsSpan(0, prefix), mask, value.AsSpan(value.Length - postfix.Value));
#else
            return value.Substring(0, prefix) + mask + value.Substring(prefix);
        }
        return value.Substring(0, prefix) + mask + value.Substring(value.Length - postfix.Value);
#endif
    }


    /// <summary>
    /// 忽略大小写比较
    /// </summary>
    public static bool EqualsIgnoreCase(this string? value, string other)
        => string.Equals(value, other, StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// 以 SqlServer 的规则比较字符串
    /// </summary>
    public static bool EqualsSqlServerString(this string? value, string other)
    => string.Compare(value, other, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth) == 0;




#if NET7_0_OR_GREATER
    /// <summary>
    /// 将字符串转换为它的等效指定类型。
    /// </summary>
    /// <typeparam name="T">目标类型</typeparam>
    /// <param name="value">要转换的字符串</param>
    /// <param name="provider">一个提供区域性特定的格式设置信息的对象</param>
    /// <param name="result">转换后的值</param>
    /// <returns>一个指示操作是否成功的返回值。</returns>
    public static bool TryParse<T>(this string? value, IFormatProvider? provider, out T? result)
        where T : IParsable<T>? => T.TryParse(value, provider, out result);
    /// <summary>
    /// 将字符串转换为它的等效指定类型
    /// </summary>
    /// <typeparam name="T">目标类型</typeparam>
    /// <param name="value">要转换的字符串</param>
    /// <param name="result">转换后的值</param>
    /// <returns>一个指示操作是否成功的返回值。</returns>
    public static bool TryParse<T>(this string? value, out T? result)
        where T : IParsable<T>? => TryParse(value, null, out result);
    /// <summary>
    /// 将字符串转换为它的等效指定类型
    /// </summary>
    /// <typeparam name="T">目标类型</typeparam>
    /// <param name="value">要转换的字符串</param>
    /// <param name="provider">一个提供区域性特定的格式设置信息的对象</param>
    /// <returns>转换后的值</returns>
    public static T Parse<T>(this string? value, IFormatProvider? provider = null)
        where T : IParsable<T>? => T.Parse(value ?? throw new ArgumentNullException(nameof(value)), provider);
    /// <summary>
    /// 将字符串转换为它的等效指定类型
    /// </summary>
    /// <typeparam name="T">目标类型</typeparam>
    /// <param name="value">要转换的字符串</param>
    /// <param name="defaultValue">默认值</param>
    /// <param name="provider">一个提供区域性特定的格式设置信息的对象</param>
    /// <returns>转换后的值或默认值</returns>
    public static T Parse<T>(this string? value, T defaultValue, IFormatProvider? provider = null)
        where T : IParsable<T>? => T.TryParse(value, provider, out var result) ? result : defaultValue;

#endif


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal static bool IsSeparator(this char c)
        => c is '_' or '-' or ' ' or '.';

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal static bool EqualsIgnoreCase(this char a, char b)
        => (a is >= 'A' and <= 'Z' ? (a + 32) : a) == (b is >= 'A' and <= 'Z' ? (b + 32) : b);

    unsafe static string ToPascalOrCamel(this string name, bool pascal)
    {
        if (string.IsNullOrEmpty(name))
        {
            return name;
        }

        int maxLen = name.Length * 2;
        char* buffer = stackalloc char[maxLen];
        int bufferIndex = 0;
        bool upperNext = pascal;

        fixed (char* pName = name)
        {
            for (int i = 0; i < name.Length; i++)
            {
                char c = pName[i];

                if (IsSeparator(c))
                {
                    if (!upperNext && bufferIndex > 0)
                    {
                        upperNext = true;
                    }
                    continue;
                }

                bool isUpper = char.IsUpper(c);
                if (isUpper)
                {
                    if (i > 0)
                    {
                        char prev = pName[i - 1];
                        bool prevIsLowerOrDigit = char.IsLower(prev) || char.IsDigit(prev);

                        if (prevIsLowerOrDigit ||
                            (char.IsUpper(prev) && i + 1 < name.Length && char.IsLower(pName[i + 1])))
                        {
                            if (!upperNext && bufferIndex > 0)
                            {
                                upperNext = true;
                            }
                        }
                    }
                    buffer[bufferIndex++] = upperNext ? c : char.ToLowerInvariant(c);
                }
                else
                {
                    buffer[bufferIndex++] = upperNext ? char.ToUpperInvariant(c) : c;
                }

                upperNext = false;
            }
        }

        return new string(buffer, 0, bufferIndex);
    }

    /// <summary>
    /// 转换为蛇形命名法（下划线分隔）。<c> user_name </c>
    /// </summary>
#if NET6_0_OR_GREATER
    [return: NotNullIfNotNull(nameof(name))]
#endif
    public static string ToSnakeCase(this string name)
    {
        if (string.IsNullOrEmpty(name))
            return name;

        unsafe
        {
            int maxLen = name.Length * 2;
            char* buffer = stackalloc char[maxLen];
            int bufferIndex = 0;
            bool lastWasSeparator = false;

            fixed (char* pName = name)
            {
                for (int i = 0; i < name.Length; i++)
                {
                    char c = pName[i];

                    if (IsSeparator(c))
                    {
                        if (!lastWasSeparator && bufferIndex > 0)
                        {
                            buffer[bufferIndex++] = '_';
                            lastWasSeparator = true;
                        }
                        continue;
                    }

                    bool isUpper = char.IsUpper(c);
                    if (isUpper)
                    {
                        if (i > 0)
                        {
                            char prev = pName[i - 1];
                            bool prevIsLowerOrDigit = char.IsLower(prev) || char.IsDigit(prev);

                            if (prevIsLowerOrDigit ||
                                (char.IsUpper(prev) && i + 1 < name.Length && char.IsLower(pName[i + 1])))
                            {
                                if (!lastWasSeparator && bufferIndex > 0)
                                {
                                    buffer[bufferIndex++] = '_';
                                }
                            }
                        }
                        buffer[bufferIndex++] = char.ToLowerInvariant(c);
                    }
                    else
                    {
                        buffer[bufferIndex++] = c;
                    }

                    lastWasSeparator = false;
                }
            }

            return new string(buffer, 0, bufferIndex);
        }
    }

    /// <summary>
    /// 转换为驼峰命名法。<c> userName </c>
    /// </summary>
#if NET6_0_OR_GREATER
    [return: NotNullIfNotNull(nameof(name))]
#endif
    public static string ToCamelCase(this string name)
        => ToPascalOrCamel(name, false);

    /// <summary>
    /// 转换为帕斯卡命名法。<c> UserName </c>
    /// </summary>
#if NET6_0_OR_GREATER
    [return: NotNullIfNotNull(nameof(name))]
#endif
    public static string ToPascalCase(this string name)
        => ToPascalOrCamel(name, true);

    /// <summary>
    /// 转换为连字符命名法。<c> user-name </c>
    /// </summary>
#if NET6_0_OR_GREATER
    [return: NotNullIfNotNull(nameof(name))]
#endif
    /// <summary>
    /// 转换为连字符命名法（kebab-case）。
    /// </summary>
    public static string ToKebabCase(this string name)
    {
        if (string.IsNullOrEmpty(name))
            return name;

        unsafe
        {
            int maxLen = name.Length * 2;
            char* buffer = stackalloc char[maxLen];
            int bufferIndex = 0;
            bool lastWasSeparator = false;

            fixed (char* pName = name)
            {
                for (int i = 0; i < name.Length; i++)
                {
                    char c = pName[i];

                    if (IsSeparator(c))
                    {
                        if (!lastWasSeparator && bufferIndex > 0)
                        {
                            buffer[bufferIndex++] = '-';
                            lastWasSeparator = true;
                        }
                        continue;
                    }

                    bool isUpper = char.IsUpper(c);
                    if (isUpper)
                    {
                        if (i > 0)
                        {
                            char prev = pName[i - 1];
                            bool prevIsLowerOrDigit = char.IsLower(prev) || char.IsDigit(prev);

                            if (prevIsLowerOrDigit ||
                                (char.IsUpper(prev) && i + 1 < name.Length && char.IsLower(pName[i + 1])))
                            {
                                if (!lastWasSeparator && bufferIndex > 0)
                                {
                                    buffer[bufferIndex++] = '-';
                                }
                            }
                        }
                        buffer[bufferIndex++] = char.ToLowerInvariant(c);
                    }
                    else
                    {
                        buffer[bufferIndex++] = c;
                    }

                    lastWasSeparator = false;
                }
            }

            return new string(buffer, 0, bufferIndex);
        }
    }

    /// <summary>
    /// 以变量名的规则比较字符串，忽略大小写和分隔符（下划线、连字符、空格、小数点）
    /// </summary>
    /// <param name="value"> 待比较的字符串 </param>
    /// <param name="other"> 用于比较的字符串 </param>
    /// <param name="ignoreSeparator"> 是否忽略分隔符 </param>
    public static bool EqualsVariableName(this string? value, string? other)
        => VariableNameComparer.Instance.Equals(value, other);
}